package org.slicerj.utils;

import java.util.Iterator;
import java.util.List;

import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;

/**
 * Compute the name of field declared at a given position from an JDOM
 * CompilationUnit.
 */
public class MethodVisitor extends ASTVisitor {

	private int fPosition;

	private String fMethodName;
	private String packageName;
	private String className;

	private String fMethodSignature;

	private boolean mFound;
	private boolean vFound;
	private String vAsgmt;

	/**
	 * Constructor
	 * 
	 * @param position
	 *            the position in the compilation unit.
	 */
	public MethodVisitor(int position) {
		fPosition = position;
		mFound = false;
		vFound = false;
		vAsgmt = "";
	}

	/**
	 * Return the name of the method declared at the given position. Return
	 * <code>null if there is no method declaration at the given position.
	 * .
	 */
	public String getMethodName() {
		return fMethodName;
	}

	/**
	 * Return the name of the method declared at the given position. Return
	 * <code>null if there is no method declaration at the given position or
	 * if not possible to compute the signature of the method declared at the given
	 * position.
	 * 
	 * @see BreakpointFieldLocator#getMethodName()
	 */
	public String getMethodSignature() {
		return fMethodSignature;
	}

	public String getVariable() {
		return vAsgmt;
	}

	public String getClassName() {
		return className;
	}

	public String getPackageName() {
		return packageName;
	}

	private String computeMethodSignature(MethodDeclaration node) {
		if (node.getExtraDimensions() != 0
				|| Modifier.isAbstract(node.getModifiers())) {
			return null;
		}
		StringBuffer signature = new StringBuffer();
		signature.append('(');
		List parameters = node.parameters();

		for (Iterator iter = parameters.iterator(); iter.hasNext();) {
			Type type = ((SingleVariableDeclaration) iter.next()).getType();
			if (type instanceof PrimitiveType) {
				appendTypeLetter(signature, (PrimitiveType) type);
			} else {
				return null;
			}
		}
		signature.append(')');
		Type returnType;
		returnType = node.getReturnType2();
		if (returnType instanceof PrimitiveType) {
			appendTypeLetter(signature, (PrimitiveType) returnType);
		} else {
			return null;
		}
		return signature.toString();
	}

	private void appendTypeLetter(StringBuffer signature, PrimitiveType type) {
		PrimitiveType.Code code = type.getPrimitiveTypeCode();
		if (code == PrimitiveType.BYTE) {
			signature.append('B');
		} else if (code == PrimitiveType.CHAR) {
			signature.append('C');
		} else if (code == PrimitiveType.DOUBLE) {
			signature.append('D');
		} else if (code == PrimitiveType.FLOAT) {
			signature.append('F');
		} else if (code == PrimitiveType.INT) {
			signature.append('I');
		} else if (code == PrimitiveType.LONG) {
			signature.append('J');
		} else if (code == PrimitiveType.SHORT) {
			signature.append('S');
		} else if (code == PrimitiveType.VOID) {
			signature.append('V');
		} else if (code == PrimitiveType.BOOLEAN) {
			signature.append('Z');
		}
	}

	/**
	 * @see org.eclipse.jdt.core.dom.ASTor#visit(org.eclipse.jdt.core.dom.CompilationUnit)
	 */
	public boolean visit(CompilationUnit node) {
		packageName = node.getPackage().getName().toString();
		// visit only the type declarations
		List types = node.types();

		for (Iterator iter = types.iterator(); iter.hasNext() && !mFound;) {
			((TypeDeclaration) iter.next()).accept(this);
		}

		return false;
	}

	/**
	 * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.TypeDeclaration)
	 */
	public boolean visit(TypeDeclaration node) {
		className = node.getName().getFullyQualifiedName();

		// if (containsPosition(node)) {
		// visit the method declarations
		MethodDeclaration[] methods = node.getMethods();
		for (int i = 0, length = methods.length; i < length && !mFound; i++) {
			MethodDeclaration m = methods[i];
			m.accept(this);
		}
		if (!mFound) {
			// visit inner types
			TypeDeclaration[] types = node.getTypes();
			for (int i = 0, length = types.length; i < length && !mFound; i++) {
				types[i].accept(this);
			}
		}
		// }
		return false;
	}

	/**
	 * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.FieldDeclaration)
	 */
	public boolean visit(MethodDeclaration node) {
		if (node.isConstructor()) {
			fMethodName = "<init>"; //$NON-NLS-1$
		} else {
			fMethodName = node.getName().getIdentifier();
		}

		fMethodSignature = computeMethodSignature(node);
		mFound = true;

		// visit the method declarations
		// List types = node.getBody().statements();
		// int z = types.size();
		//
		// for (Iterator iter = types.iterator(); iter.hasNext() && !vFound;) {
		// Object o = iter.next();
		//
		// if (o instanceof ExpressionStatement) {
		// ((ExpressionStatement) o).accept(this);
		// } else if (o instanceof VariableDeclarationStatement) {
		// ((VariableDeclarationStatement) o).accept(this);
		// }
		// }
		return false;
	}

	public boolean visit(VariableDeclarationStatement node) {

		// if (containsPosition(node)) {
		vAsgmt = node.toString();
		vFound = true;
		// }
		return false;
	}

	public boolean visit(ExpressionStatement node) {
		vAsgmt = node.toString();
		vFound = true;

		return false;
	}

}